/* 
*  Copyright (C) 2009-2010 WCG Project
*
*  This program is free software; you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by the
*  Free Software Foundation; either version 2 of the License, or (at
*  your option) any later version.
*
*  This program is distributed in the hope that it will be useful, but
*  WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software Foundation,
*  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*  In addition, as a special exception, the author gives permission to
*  link the code of this program with the Half-Life Game Engine ("HL
*  Engine") and Modified Game Libraries ("MODs") developed by Valve,
*  L.L.C ("Valve"). You must obey the GNU General Public License in all
*  respects for all of the code used other than the HL Engine and MODs
*  from Valve. If you modify this file, you may extend this exception
*  to your version of the file, but you are not obligated to do so. If
*  you do not wish to do so, delete this exception statement from your
*  version.
*/




//-------------------------------------------
// PLAYER_InitClass
//
// Function called right after the user's class information is set
//-------------------------------------------
PLAYER_InitClass( id )
{
	// Do we need to give this user XP?
	new iStartLevel = get_pcvar_num( CVAR_start_level )
	if( PlayerInfo[id][PI_XP] == 0 && iStartLevel > 0 && iStartLevel <= MAX_LEVELS )
	{
		PlayerInfo[id][PI_XP] = XP_GetByLevel( iStartLevel )
		client_print( id, print_chat, "%s Your race has started off at level %d!", g_MODclient, iStartLevel )
	}

	// See if there are any skills available
	if( SM_SkillsAvailable( id ) )
		MENU_SelectSkill( id )
	else if( GetAvailableAttributePoints( id ) )
		MENU_SelectAttribute( id )

	
	XP_Check( id, false )

	WC3_ShowBar( id )
	WCG_ShowClassInfo( id )

	InitAttributeEffects( id )
}


//-------------------------------------------
// PLAYER_SetDefaults
//
// This function can be called at ANY time to configure and/or remove skills for a given race
//-------------------------------------------
PLAYER_SetDefaults( id )
{
	PLAYER_UpdateSpeed( id )
	PLAYER_UpdateGravity( id )
	PLAYER_UpdateInvisibility( id )
}



//-------------------------------------------
// PLAYER_Damage
//
//-------------------------------------------
stock PLAYER_Damage( Float:fDelay, iVictim, iAttacker, iDamage, iWeapon, iBodyPart, iDamageType = DMG_GENERIC)
{
	static param[6]
	param[0] = iVictim
	param[1] = iAttacker
	param[2] = iDamage
	param[3] = iWeapon
	param[4] = iBodyPart
	param[5] = iDamageType

	set_task( fDelay, "_PLAYER_Damage", TASK_DELAYED_DAMAGE, param, 6 )
}


//-------------------------------------------
// PLAYER_MagicDamage
//
//-------------------------------------------
stock PLAYER_MagicDamage( iVictim, iAttacker, iBaseDamage, iWeapon, iDamageType = DMG_DROWN, Float:fDelay = 0.0)
{
	static param[6], Float:fDamage, Float:fDiv
	
	if( !is_user_alive( iAttacker ) )
		return

	fDamage = float( iBaseDamage )
	fDiv = float(MAX_LEVELS+MAX_ATTRIBUTELEVEL*4)
	fDamage *= 1.0+(float(PlayerInfo[iAttacker][PI_Level] + PlayerInfo[iAttacker][PI_INT]*4))/fDiv*0.8

	if( PlayerBuffInfo[iAttacker][BUFF_BeastWithin] == true )
	{
		new iSkillLevel = SM_GetSkillLevel( iAttacker, ULTIMATE_BEASTWITHIN )
		fDamage *= p_beastwithin[iSkillLevel-1]
	}


//	debug_print( "dmg = %d", floatround( fDamage) )

	param[0] = iVictim
	param[1] = iAttacker
	param[2] = floatround( fDamage )
	param[3] = iWeapon
	param[4] = HIT_GENERIC
	param[5] = iDamageType

	set_task( fDelay, "_PLAYER_Damage", TASK_DELAYED_DAMAGE, param, 6 )
}



//-------------------------------------------
// _PLAYER_Damage
//
// Task function called by PLAYER_Damage and PLAYER_MagicDamage
//-------------------------------------------
public _PLAYER_Damage( const param[] )
{
	static iVictim, iAttacker, iDamage, iWeapon, iDamageType

	iVictim 							= param[0]
	iAttacker 							= param[1]
	iDamage 							= param[2]
	iWeapon 							= param[3]
	PlayerInfo[iVictim][PI_Hitgroup]	= param[4]
	iDamageType 						= param[5]

	if( !is_user_alive( iVictim ) || !is_user_connected( iAttacker ) )
		return

	UTIL_GetWeaponName( iWeapon, szTmpMsg, 31 )

	new ent = fm_create_entity("info_target")

	set_pev( ent, pev_owner, iAttacker )
	set_pev( ent, pev_iuser1, iWeapon )
	set_pev( ent, pev_classname, szTmpMsg )

	ExecuteHamB( Ham_TakeDamage, iVictim, ent, iAttacker, float(iDamage), iDamageType )

	fm_remove_entity( ent )
}



//-------------------------------------------
// PLAYER_Heal
//
// Returns how much health that was actually given
//-------------------------------------------
PLAYER_Heal( id, iHealth, iGiverID=0 )
{
	static vOrigin[3], Float:fCurrentHealth, MsgLock[33]

	if( !is_user_alive( id ) )
		return 0

	pev( id, pev_health, fCurrentHealth )

	new Float:fMaxHealth = float( PLAYER_GetMaxhealth( id ) )
	new Float:fHealth = float( iHealth )

	if( fCurrentHealth < fMaxHealth )
	{
		if( fCurrentHealth+fHealth > fMaxHealth )
		{
			set_pev( id, pev_health, fMaxHealth )
			iHealth = floatround( fMaxHealth - fCurrentHealth )
		}
		else
			set_pev( id, pev_health, fCurrentHealth+fHealth )
		
		get_user_origin( id, vOrigin )
		
		// Ensure we only send this effect once per second
		if( MsgLock[id] != GameInfo[GI_Heartbeat] )
		{
			if( PlayerInfo[id][PI_Stealthed] )
				EFFECT_Implosion( id, vOrigin, 100, 8, 1 )
			else
				EFFECT_Implosion( 0, vOrigin, 100, 8, 1 )
		}		

		MsgLock[id] = GameInfo[GI_Heartbeat]

		if( iGiverID )
		{
			if( is_user_alive(iGiverID) )
				g_iHealingDone[iGiverID][id] += iHealth
		}
		return iHealth
	}
	return 0
}




//-------------------------------------------
// PLAYER_CheckMagicHit
//
// Checks if the attacker hits with a magic attack or if it was completely resisted
//-------------------------------------------
PLAYER_CheckMagicHit( iAttacker, iVictim )
{
	static const MaxRating = ((MAX_LEVELS*2+(MAX_ATTRIBUTELEVEL+10)*10)+MAX_LEVELS*2)
	static Float:fRnd

	new Float:fAttackerLevel	= float( PlayerInfo[iAttacker][PI_Level] * 2 )
	new Float:fVictimLevel		= float( PlayerInfo[iVictim][PI_Level] * 2)
	new Float:fAttackerINT		= float( PlayerInfo[iAttacker][PI_INT] * 10 )
	new Float:fVictimSPR		= float( PlayerInfo[iVictim][PI_SPR] * 10 )

	new Float:baseResist = (fVictimSPR + fVictimLevel)
	
	if( ITEM_Has( iVictim, ITEM_NECKLACE ) != ITEM_NONE )
		baseResist += 100.0
	
	new Float:rating = baseResist
	rating += floatmax( (fVictimLevel - fAttackerLevel), 0.0)
	rating -= floatmin( fAttackerINT + fAttackerLevel, baseResist )
	rating = ( 1.0 - rating / float(MaxRating) ) * 0.75
	rating = floatclamp( rating, 0.10, 0.90 )

	fRnd = random_float( 0.0, 1.0 )

	if( fRnd <= rating )
		return true

	return false
}


//-------------------------------------------
// PLAYER_GetResistMagicModifier
//
// Returns a magic resist modifier to be used with base magic damage.
// The modifier is a value between 0.0-1.0 where 0.0 is full resist and 1.0 is no resist
//-------------------------------------------
/*Float:PLAYER_GetResistMagicModifier( iAttacker, iVictim )
{
	static Float:fResistProbability[10]
	
//	new szArg1[32], szArg2[32], szArg3[32], szArg4[32]
//	read_argv( 1, szArg1, 31 )
//	read_argv( 2, szArg2, 31 )
//	read_argv( 3, szArg3, 31 )
//	read_argv( 4, szArg4, 31 )
//	new Float:fAttackerLevel	= float( str_to_num( szArg1 ) * 2)
//	new Float:fVictimLevel		= float( str_to_num( szArg2 ) * 2)
//	new Float:fAttackerINT		= float( str_to_num( szArg3 ) * 10 )
//	new Float:fVictimSPR		= float( str_to_num( szArg4 ) * 10 )

	new Float:fAttackerLevel	= float( PlayerInfo[iAttacker][PI_Level] * 2)
	new Float:fVictimLevel		= float( PlayerInfo[iVictim][PI_Level] * 2)
	new Float:fAttackerINT		= float( PlayerInfo[iAttacker][PI_INT] * 10 )
	new Float:fVictimSPR		= float( PlayerInfo[iVictim][PI_SPR] * 10 )

	fAttackerLevel = floatmax( fAttackerLevel, 1.0 )
	fVictimLevel = floatmax( fVictimLevel, 1.0 )

	new Float:fAvgRes = ( (fVictimSPR + fVictimLevel) / (fAttackerINT+fAttackerLevel) ) * 0.75

	for( new i = 0 ; i < 10 ; i++ )
	{
		fResistProbability[i] = floatmax( 0.5 - 2.5 * (i * 0.1 - fAvgRes), 0.0 )
		fResistProbability[i] = floatmin( fResistProbability[i], 75.0 )
	}

	log_amx( "------------------------------------------" )
	log_amx( "10% = %0.2f%", fResistProbability[0] )
	log_amx( "20% = %0.2f%", fResistProbability[1] )
	log_amx( "30% = %0.2f%", fResistProbability[2] )
	log_amx( "40% = %0.2f%", fResistProbability[3] )
	log_amx( "50% = %0.2f%", fResistProbability[4] )
	log_amx( "60% = %0.2f%", fResistProbability[5] )
	log_amx( "70% = %0.2f%", fResistProbability[6] )
	log_amx( "80% = %0.2f%", fResistProbability[7] )
	log_amx( "90% = %0.2f%", fResistProbability[8] )
	log_amx( "100% = %0.2f%",fResistProbability[9] )
	log_amx( "------------------------------------------" )

	return 0.0
}
*/



//-------------------------------------------
// PLAYER_GetMaxhealth
//
//-------------------------------------------
PLAYER_GetMaxhealth( id )
{
	new maxHealth = max( 100, PlayerInfo[id][PI_BaseHealth] )

//	new iSkillLevel = SM_GetSkillLevel( id, SKILL_DEVOTION )
//	if( iSkillLevel > 0 )
//		maxHealth += p_devotion[iSkillLevel-1]

	// Player has a health bonus from the Periapt of Health
	if( ITEM_Has( id, ITEM_HEALTH ) > ITEM_NONE )
		maxHealth += get_pcvar_num( CVAR_health )

	maxHealth += PlayerBuffInfo[id][BUFF_Fortitude]
	maxHealth += PlayerBuffInfo[id][BUFF_DevotionAura]

	return maxHealth
}



//-------------------------------------------
// PLAYER_UpdateInvisibility
//
// This should be called on weapon change, on new round, when the user selects a new skill, and after an item is purchased
//-------------------------------------------
public PLAYER_UpdateInvisibility( id )
{
	if( !is_user_alive(id) )
		return

	new iInvisLevel = 0
	new bool:bIsInvisible = false

	if( PlayerInfo[id][PI_Stealthed] > 0 )
	{
		bIsInvisible = true
		fm_set_user_rendering( id, kRenderFxNone, 0, 0, 0, kRenderTransTexture, 0 )
		return
	}

 	if( !PlayerInfo[id][PI_CanRender] )
 		return
 
	if( bIsInvisible )
	{
		fm_set_user_rendering( id, kRenderFxNone, 0, 0, 0, kRenderTransTexture, iInvisLevel )
		PlayerInfo[id][PI_IsInvisible] = true
	}

	// User should not be invisible
	else
	{
		fm_set_user_rendering( id )
		PlayerInfo[id][PI_IsInvisible] = false
	}
}


//-------------------------------------------
// PLAYER_UpdateSpeed
//
// Sets the user's speed, should be called after freezetime, on weapon change and after a speed modifying skill has been called
//-------------------------------------------
public PLAYER_UpdateSpeed( id )
{
	static Float:fNewSpeed, iSkillLevel

	id = GetTaskID( id, TASK_DELAYEDFUNCTION )

	if( !is_user_connected(id) )
		return
	
	// We should NOT change the user's speed during freezetime
	if( GameInfo[GI_FreezeTime] )
		return

	else if( PlayerInfo[id][PI_IsImmobilized] )
	{
		fm_reset_user_velocity( id )
		
		if( PlayerInfo[id][PI_IsOnGround] )
			fm_set_user_maxspeed( id, 1.0 )
		else
			fm_set_user_maxspeed( id, -1.0 )

		PLAYER_UpdateGravity( id )
		return
	}

	// User is stunned, we shouldn't change their speed
	else if( PlayerInfo[id][PI_IsStunned] )
	{
		fm_reset_user_velocity( id )
		fm_set_user_maxspeed( id, 1.0 )
		return
	}

	else if( PlayerInfo[id][PI_IsPolymorphed] )
	{
		fm_set_user_maxspeed( id, 200.0 )
		return
	}

	// User is slowed
	else if( PlayerInfo[id][PI_Slowed] > 0 )
	{
		fm_set_user_maxspeed( id, float( max( PlayerInfo[id][PI_Slowed], 130 ) ) )
		return
	}
	
	else if( PlayerInfo[id][PI_Stealthed] )
	{
		fm_set_user_maxspeed( id, (130.0 - 18.0) + (float(PlayerInfo[id][PI_Stealthed]) * 18.0) )
		return
	}

	new iClip, iAmmo
	new iWeapon = get_user_weapon( id, iClip, iAmmo )
	new Float:fBaseSpeed = 250.0

	if ( PlayerInfo[id][PI_PlayerRole] == PLR_VIP )
		fBaseSpeed = CS_SPEED_VIP

	else if ( PlayerInfo[id][PI_IsZoomed] )
		fBaseSpeed = CS_WEAPON_SPEED_ZOOM[iWeapon]

	else
		fBaseSpeed = CS_WEAPON_SPEED[iWeapon]


	fNewSpeed = fBaseSpeed + ( float(PlayerInfo[id][PI_DEX]) )
	
	// Sprint bonus
	iSkillLevel = SM_GetSkillLevel( id, SKILL_SPRINT )
	if( iSkillLevel > 0 )
		fNewSpeed += p_sprint[iSkillLevel-1]

	// Boots of Speed bonus
	if( ITEM_Has( id, ITEM_BOOTS ) > ITEM_NONE )
	{
		new iClip, iAmmo
		new iWeapon = get_user_weapon( id, iClip, iAmmo )
		
		// Then just apply the bonus!
		if ( fNewSpeed > 0.0 )
		{
			fNewSpeed *= ( 1.0 + get_pcvar_float( CVAR_boots ) )
		}
		
		// User only has boots
		else
		{
			// Give them the bonus
			if ( PlayerInfo[id][PI_PlayerRole] == PLR_VIP )
				fNewSpeed = CS_SPEED_VIP * ( 1.0 + get_pcvar_float( CVAR_boots ) )

			// Player zoomed in
			else if ( PlayerInfo[id][PI_IsZoomed] )
				fNewSpeed = CS_WEAPON_SPEED_ZOOM[iWeapon] * ( 1.0 + get_pcvar_float( CVAR_boots ) )

			// Regular
			else
				fNewSpeed = CS_WEAPON_SPEED[iWeapon] * ( 1.0 + get_pcvar_float( CVAR_boots ) )
		}
	}

	// Change the user's speed! cap at 320.0
	if ( fNewSpeed > 0.0 )
		fm_set_user_maxspeed( id, floatmin( fNewSpeed, 320.0 ) )
}


//-------------------------------------------
// PLAYER_UpdateGravity
//
//-------------------------------------------
public PLAYER_UpdateGravity( id )
{
	// Can't set gravity if user isn't connected!
	if( !is_user_connected(id) )
		return

	else if( PlayerInfo[id][PI_IsImmobilized] )
	{
		if( pev(id, pev_flags) & FL_ONGROUND )
			set_pev( id, pev_gravity, 999999999.9 )
		else
			set_pev( id, pev_gravity, 0.000000001 )
		return
	}
	
	// If gravity is less than this, lets not change per-user b/c it BLOWS ASS in game
	else if ( CVAR_sv_gravity == 0 || get_pcvar_num( CVAR_sv_gravity ) > 650 )
	{
		static iSkillLevel
		iSkillLevel = SM_GetSkillLevel( id, SKILL_LEVITATION )

		new Float:fGravityLevel = 1.0

		if( PlayerInfo[id][PI_IsPolymorphed] )
		{
			fm_set_user_gravity( id, fGravityLevel * 0.7 )
			return
		}

		// If this user does want their gravity to be changed!
		if( ( iSkillLevel > 0 ) && g_bLevitation[id] )
			fGravityLevel = p_levitation[iSkillLevel-1]

		// Set the user's gravity based on the item
		if( ITEM_Has( id, ITEM_SOCK ) > ITEM_NONE )
		{
			// User has levitation + sock, give them an extra bonus
			if( fGravityLevel < 1.0 )
				fGravityLevel *= 0.5

			// User just has sock
			else
				fGravityLevel = get_pcvar_float( CVAR_sock )
		}

		// Set the user's gravity!
		fm_set_user_gravity( id, fGravityLevel )
	}
}



//-------------------------------------------
// PLAYER_Spawn
//
// Function used to spawn a player
//-------------------------------------------
PLAYER_Spawn( id )
{
	if( !IsValidUser(id) )
		return
		
	if( !is_user_connected( id ) || is_user_alive( id) || !IsOnTeam(id) )
		return

	// Round has ended, lets give money back if they bought a scroll
	if( GameInfo[GI_EndRound] )
	{
		if ( PlayerInfo[id][PI_RespawnBy] == RESPAWN_ITEM )
		{
			client_print( id, print_chat, "%s Unable to respawn because the round is over, here is your money back", g_MODclient )
			cs_set_user_money( id, cs_get_user_money( id ) + ITEM_Cost( id, ITEM_SCROLL ) )
		}
		return
	}

	// Make a copy of the players saved weapons
	SHARED_CopySavedWeapons( id )

	// Ignore the armor setting...
	bIgnoreArmorSet[id] = true

	// Spawn the player
	ExecuteHam( Ham_CS_RoundRespawn, id )
	
	PlayerInfo[id][PI_Slowed]		= 0
	PlayerInfo[id][PI_IsStunned]	= false
	PlayerInfo[id][PI_GiveItems]	= true

	// Reset items when the user spawns!
	g_iShopMenuItems[id][ITEM_SLOT_ONE]	= ITEM_NONE
	g_iShopMenuItems[id][ITEM_SLOT_TWO] = ITEM_NONE

	// Reset the user's skin to normal
	CS_ChangeSkin( id, SKIN_RESET )

	set_task( 0.2, "_PLAYER_Spawn_Final", TASK_SPAWNPLAYER + id )
	set_task( 0.4, "_PLAYER_GiveWeapons", TASK_DELAYEDFUNCTION + id )
}


//-------------------------------------------
// _PLAYER_Spawn_Final
//
// Function will just spawn a player again
//-------------------------------------------
public _PLAYER_Spawn_Final( id )
{
	id = GetTaskID( id, TASK_SPAWNPLAYER )
	
	new iDeaths

	if( !is_user_connected(id) )
		return

	bIgnoreArmorSet[id] = false

	// If we don't give them a suit then they won't have a HUD
	fm_give_item( id, "item_suit" )

	WCG_UpdateStatusBar( id )
	PLAYER_SetDefaults( id )

	// Soulstone Check
	if( PlayerInfo[id][PI_RespawnBy] == RESPAWN_SOULSTONE )
	{
		new iSkillLevel = SM_GetSkillLevel( id, SKILL_SOULSTONE ) 
		if( iSkillLevel > 0 )
		{
			new Float:fMod = p_soulstone[iSkillLevel-1]
			new hp = floatround( float( PLAYER_GetMaxhealth(id) ) * fMod )
			fm_set_user_health( id, hp )
			
			iDeaths = fm_get_user_deaths( id )
			fm_set_user_deaths( id, max( iDeaths-1, 0 ) )
		}
		SM_SetCooldown( id, SKILL_SOULSTONE )

		if( PlayerInfo[id][PI_HadAnkh] )
			ITEM_GiveItem( id, ITEM_ANKH )
	}

	else if( PlayerInfo[id][PI_RespawnBy] == RESPAWN_RESURRECT )
	{
		new iSkillLevel = PlayerInfo[id][PI_ResurrectLevel]
		if( iSkillLevel <= 0 || iSkillLevel > g_SkillMaxLevel[SKILL_RESURRECT] )
			return

		new Float:fMod = p_resurrect[iSkillLevel-1]
		new hp = floatround( float( PLAYER_GetMaxhealth(id) ) * fMod )
		fm_set_user_health( id, hp )

		iDeaths = fm_get_user_deaths( id )
		fm_set_user_deaths( id, max( iDeaths-1, 0 ) )

		if( PlayerInfo[id][PI_HadAnkh] )
			ITEM_GiveItem( id, ITEM_ANKH )
	}
}





//-------------------------------------------
// PLAYER_CheckEqReincarnation
//
// Reincarnation for Counter-Strike and Condition Zero
//-------------------------------------------
public PLAYER_CheckEqReincarnation( id )
{
	if( !is_user_connected(id) || !is_user_alive(id) )
		return

	new bool:bGiveWeapons = false
	
	// If the give items flag is true
	if( PlayerInfo[id][PI_GiveItems] )
	{
		bGiveWeapons = true
		PlayerInfo[id][PI_GiveItems] = false
	}

	// Check based on skill or if the user has an item
	if( PlayerInfo[id][PI_DiedLastRound] )
	{
		new iSkillLevel = SM_GetSkillLevel( id, SKILL_REINCARNATION )

		// Ankh
		if( g_bPlayerBoughtAnkh[id] )
		{
			bGiveWeapons = true
			g_bPlayerBoughtAnkh[id] = false
		}
		else if( iSkillLevel > 0 )
		{
			if( random_float( 0.0, 1.0 ) <= p_ankh[iSkillLevel-1] )
				bGiveWeapons = true		
		}
	}

	if( bGiveWeapons )
	{
		client_cmd( id, "speak %s", g_szSounds[SOUND_REINCARNATION] )

		// Screen fade green
		EFFECT_ScreenFade( id, 0.25, 0.25, COLOR_RGBA(0, 255, 0, 255) )
		
		// Give the user their weapons from last round
		set_task( 0.3, "_PLAYER_GiveWeapons", TASK_DELAYEDFUNCTION + id )
	}
	else
	{
		if( !cs_get_user_nvg( id ) )
			PlayerInfo[id][PI_Nightvision] = false
	}
}



//-------------------------------------------
// _PLAYER_GiveWeapons
//
//-------------------------------------------
public _PLAYER_GiveWeapons(id)
{
	id = GetTaskID( id, TASK_DELAYEDFUNCTION )

	new bool:bGiveBomb = false

	if ( !is_user_connected(id) || !is_user_alive(id) )
		return

	// Check to see if the user should have the bomb
	if( cs_get_user_plant( id ) )
		bGiveBomb = true

	// Remove all weapons
	fm_strip_user_weapons( id )

	// Give armor
	if( PlayerInfo[id][PI_LastArmor] )
		cs_set_user_armor( id, PlayerInfo[id][PI_LastArmor], PlayerInfo[id][PI_ArmorType] )		// g_ArmorType
	
	// Give a defuse kit
	if( PlayerInfo[id][PI_Defuse] )
	{
		fm_give_item(id, "item_thighpack")

		// Check for bomb on the back of chameleon
		if ( PlayerInfo[id][PI_SkinSwitched] )
			set_pev( id, pev_body, 0 )
	}

	if( PlayerInfo[id][PI_Nightvision] )
		cs_set_user_nvg( id, 1 )

	// Give the user a bomb
	if( bGiveBomb )
	{
		fm_give_item( id, "weapon_c4" )
		cs_set_user_plant( id )
	}
	
	new iWeapID = 0, i = 0, bool:bPrimaryFound = false
	for( i = 0; i < 32; i++ )
	{
		iWeapID = g_PlayerLastWeapons[id][i]

		if( iWeapID )
		{
			if( iWeapID != CSW_C4 && iWeapID != CSW_FLASHBANG )
			{
				static szWeaponName[32]
				
				get_weaponname( iWeapID, szWeaponName, 31 )

				if( contain( szWeaponName, "weapon_" ) == 0 )
				{
					fm_give_item( id, szWeaponName )
					fm_set_user_bpammo( id, iWeapID )
				}
			}
		}

		if( cs_is_primary_weapon( iWeapID ) )
			bPrimaryFound = true
	}

	// Only give it if the user doesn't have a primary weapon
	if( PlayerInfo[id][PI_Shield] )
	{
		if ( !bPrimaryFound )
			fm_give_item( id, "weapon_shield" )
	}

	// Give them their flash bangs back
	while( PlayerInfo[id][PI_FlashCount] > 0 )
	{
		fm_give_item( id, "weapon_flashbang" )
		PlayerInfo[id][PI_FlashCount]--
	}

	set_task( 0.3, "_PLAYER_SetSilenceBurst", TASK_DELAYEDFUNCTION + id )
}



//-------------------------------------------
// _PLAYER_SetSilenceBurst
//
//-------------------------------------------
public _PLAYER_SetSilenceBurst( id )
{
	id = GetTaskID( id, TASK_DELAYEDFUNCTION )

	if ( !is_user_connected(id) )
		return

	new iWeaponEnt

	iWeaponEnt = fm_find_ent_by_owner( -1, "weapon_famas", id )
	if ( pev_valid( iWeaponEnt ) )
		cs_set_weapon_burst( iWeaponEnt, PlayerInfo[id][PI_FamasBurst] )

	iWeaponEnt = fm_find_ent_by_owner( -1, "weapon_glock18", id )
	if ( pev_valid( iWeaponEnt ) )
		cs_set_weapon_burst( iWeaponEnt, PlayerInfo[id][PI_GlockBurst] )
}




//-------------------------------------------
// PLAYER_CastSpell
//
//-------------------------------------------
bool:PLAYER_CastSpell( id, iSkillID, const Type=CAST_NORMAL, const szTaskString[]="", const sParam[]="", const sParamSize=0, const Flags=0 )
{
	new Float:fCastTime, iManaCost

	if( !IsValidSkill( iSkillID ) )
	{
		WC3_Log( false, "[PLAYER_CastSpell] Invalid skill: %d", iSkillID )
		log_error( AMX_ERR_NATIVE, "[PLAYER_CastSpell] Invalid skill: %d", iSkillID )
		return false
	}
	
	if( sParamSize >= 64 )
	{
		WC3_Log( false, "[PLAYER_CastSpell] Invalid sParamSize in SM_CastSpell() call: %d (max 63)", sParamSize )
		log_error( AMX_ERR_NATIVE, "[PLAYER_CastSpell] Invalid sParamSize in SM_CastSpell() call: %d (max 63)", sParamSize  )
		return false
	}

	fCastTime = SM_GetSkillCastTime( iSkillID )
	iManaCost = SM_GetManaCost( id, iSkillID )

	PlayerInfo[id][PI_LastManaCost]	= 0

	if( PlayerInfo[id][PI_SpellCooldown+(S_PLAYERINFO:iSkillID)] > 0 && !(Flags & CAST_IGNORE_COOLDOWN) )
	{
		PLAYER_SpellAborted( id, iSkillID, SPELL_ERROR_COOLDOWN, Type )
		return false
	}

	// Are we already casting a spell? Passive spells always goes through
	else if( PlayerInfo[id][PI_CastingSpell] && Type != CAST_PASSIVE )
		return false

	// Can't cast while planting or defusing bomb
	else if( PlayerInfo[id][PI_IsPlantingDefusing] && !(Flags & CAST_IGNORE_PLANT_DEFUSE) )
	{
		PLAYER_SpellAborted( id, iSkillID, SPELL_ERROR_PLANTDEFUSE, Type )
		return false
	}

	else if ( PlayerInfo[id][PI_IsStunned] && !(Flags & CAST_IGNORE_STUNNED) )
	{
		PLAYER_SpellAborted( id, iSkillID, SPELL_ERROR_STUNNED, Type )
		return false
	}

	else if ( PlayerInfo[id][PI_IsPolymorphed]  )
	{
		PLAYER_SpellAborted( id, iSkillID, SPELL_ERROR_POLYMORPHED, Type )
		return false
	}

	else if( PlayerInfo[id][PI_Mana] < iManaCost && !(Flags & CAST_IGNORE_MANA_COST) )
	{
		if( !(Flags & CAST_HIDE_MANA_WARNING) )
			PLAYER_SpellAborted( id, iSkillID, SPELL_ERROR_NO_MANA, Type )
		return false
	}


	if( Flags & CAST_SIMULATE )
	{
		if( Type == CAST_NORMAL && (!(Flags & CAST_IGNORE_MOVEMENT) && fCastTime > 0.1) )
		{
			if( PlayerInfo[id][PI_Speed] > MAX_CASTING_VELOCITY )
			{
				WCG_StatusText( id, 0, HUD_CASTING, "You can't cast while moving!" )
				return false
			}
		}
		return true
	}

	switch( Type )
	{
		// Passive casts. Does not interrupt any current casts
		case CAST_PASSIVE:
		{
			if( !(Flags & CAST_SKIP_SET_MANA_COST) )
			{
				PlayerInfo[id][PI_LastManaCost] = iManaCost
				PlayerInfo[id][PI_fMana] -= float(iManaCost)
				PlayerInfo[id][PI_Mana] = clamp( floatround(PlayerInfo[id][PI_fMana]), 0, PlayerInfo[id][PI_MaxMana] )
			}
		
			if( !(Flags & CAST_SKIP_SET_COOLDOWN) )
				SM_SetCooldown( id, iSkillID )
		
			set_task( fCastTime, szTaskString, _, sParam, sParamSize )
			return true
		}
		
		// Searching casts
		case CAST_SEARCH:
		{
			if ( !PlayerInfo[id][PI_IsSearching] && !task_exists( TASK_SEARCHPING + id ) )
			{
				if( ((Flags & CAST_SEARCH_OPPOSITE_TEAM) && (Flags & CAST_SEARCH_SAME_TEAM)) || \
					!(Flags & CAST_SEARCH_OPPOSITE_TEAM | CAST_SEARCH_SAME_TEAM ) )
					PlayerInfo[id][PI_SearchTeamID] = TEAM_UNASSIGNED
					
				else if(Flags & CAST_SEARCH_OPPOSITE_TEAM)
					PlayerInfo[id][PI_SearchTeamID] = (PlayerInfo[id][PI_TeamID] == TEAM_T ) ? TEAM_CT : TEAM_T
					
				else if(Flags & CAST_SEARCH_SAME_TEAM)
					PlayerInfo[id][PI_SearchTeamID] = PlayerInfo[id][PI_TeamID]
			
				PlayerInfo[id][PI_IsSearching] = true
				PlayerInfo[id][PI_SearchSkillID] = iSkillID
				PlayerInfo[id][PI_SearchFlags] = Flags
				copy( PlayerInfo[id][PI_SearchTaskName], 31, szTaskString )
	
				// Flash their ultimate icon
				ULT_Icon( id, ICON_FLASH )

				new param2[2]
				param2[0] = id
				param2[1] = 5
				_PLAYER_SearchPing( param2 )
			}
			return true
		}
		
		// Normal casts
		case CAST_NORMAL:
		{
			static param[128]
			
			param[0] = id
			param[1] = Flags
			param[2] = iManaCost
			param[3] = _:fCastTime
			param[4] = iSkillID
			param[5] = Type
			copy( param[6], 31, szTaskString )
			param[38] = sParamSize
			for( new i = 0 ; i < sParamSize ; i++ )
				param[i+39] = sParam[i]
				
			if( !(Flags & CAST_IGNORE_MOVEMENT) && fCastTime > 0.1 )
			{
				// Can't cast while moving. 130 is normal walkspeed
				if( PlayerInfo[id][PI_Speed] > MAX_CASTING_VELOCITY )
				{
					WCG_StatusText( id, 0, HUD_CASTING, "You can't cast while moving!" )
					return false
				}
			}
		
			PlayerInfo[id][PI_CastingSpell] = true
			PlayerInfo[id][PI_CastingInterrupted] = false

			if( PlayerInfo[id][PI_Stealthed] )
				_RemoveStealth( id )

			// Stop any search spells if we're starting on a spell with casttime
			if( PlayerInfo[id][PI_IsSearching] )
			{
				ULT_Icon( id, ICON_SHOW )
				PlayerInfo[id][PI_IsSearching] = false
				remove_task( TASK_SEARCHPING + id )
			}

			if( fCastTime > 0.1 )
			{
				LANG_GetSkillName( iSkillID, id, szTmpMsg, 63 )
				WCG_StatusText( id, TXT_CASTING, HUD_CASTING, "*** Casting %s ***", szTmpMsg )
		
				Create_BarTime( id, floatround(fCastTime), 0 );
				SHARED_Glow( id, COLOR_RGB( 0, 50, 255 ) )
				
				new param2[2]
				param2[0] = id
				param2[1] = Flags
				set_task( 0.1, "_PLAYER_CheckCasting", TASK_CASTINGINTERRUPTCHECK + id, param2, sizeof(param2), "b" )
			}
		
			if( fCastTime > 0.0 )
			{
				emit_sound( id, CHAN_VOICE, g_szSounds[SOUND_PRECAST_HOLY], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )
				set_task( fCastTime, "_PLAYER_EndCasting", TASK_CASTINGSPELL + id, param, sizeof(param) )
			}
			else
				_PLAYER_EndCasting( param )

			return true
		}
	}
	
	WC3_Log( false, "[0] Unknown spell type: %d", Type )
	log_error( AMX_ERR_NATIVE, "[0] Unkown spell type: %d", Type )
	return false
}



//-------------------------------------------
// _PLAYER_CheckCasting
//
//-------------------------------------------
public _PLAYER_CheckCasting( const param[] )
{
	static id, Flags
	new bool:bRemoveTasks = false
	
	id = param[0]
	Flags = param[1]
	
	if( !is_user_alive(id) )
		bRemoveTasks = true
	
	else if( !PlayerInfo[id][PI_CastingSpell] )
		bRemoveTasks = true

	// Check to see if the cast was interrupted
	else if( PlayerInfo[id][PI_CastingInterrupted] && !(Flags & CAST_IGNORE_INTERRUPT) )
	{
		bRemoveTasks = true

		// Remove the castbar
		Create_BarTime( id, 0, 0 )

		WCG_StatusText( id, TXT_CASTING, HUD_CASTING, "*** CASTING INTERRUPTED ***" )

		// Stop casting sound
		emit_sound( id, CHAN_VOICE, g_szSounds[SOUND_NULL], 0.0, ATTN_NORM, 0, PITCH_NORM )
	}

	if( bRemoveTasks )
		PLAYER_RemoveSpellCasting( id )
}



//-------------------------------------------
// _PLAYER_EndCasting
//
//-------------------------------------------
public _PLAYER_EndCasting( const param[] )
{
	new id, Flags, iManaCost, iSkillID, Float:fCastTime, szTaskString[32]
	new sParam[64], sParamSize

	id = param[0]
	Flags = param[1]
	iManaCost = param[2]
	fCastTime = Float:param[3]
	iSkillID = param[4]
	copy( szTaskString, 31, param[6] )
	sParamSize = param[38]
	for( new i = 0 ; i < sParamSize ; i++ )
		sParam[i] = param[i+39]

	PLAYER_RemoveSpellCasting( id )

	if( (PlayerInfo[id][PI_Mana] < iManaCost) && !(Flags & CAST_IGNORE_MANA_COST) )
	{
		if( !(Flags & CAST_HIDE_MANA_WARNING) )
			PLAYER_SpellAborted( id, iSkillID, SPELL_ERROR_NO_MANA )
		return
	}
	
	if( !(Flags & CAST_SKIP_SET_MANA_COST) )
	{
		PlayerInfo[id][PI_LastManaCost] = iManaCost
		PlayerInfo[id][PI_fMana] -= float(iManaCost)
		PlayerInfo[id][PI_Mana] = clamp( floatround(PlayerInfo[id][PI_fMana]), 0, PlayerInfo[id][PI_MaxMana] )
	}

	// Only clear the casting HUD channel if it wasn't an instant spell
	if( fCastTime > 0.1 )
		UTIL_ClearHudChannel(id, HUD_CASTING )


	if( !(Flags & CAST_SKIP_SET_COOLDOWN) )
		SM_SetCooldown( id, iSkillID )

	if( !(Flags & CAST_SKIP_ENDCAST_SOUND) )
		emit_sound( id, CHAN_VOICE, g_szSounds[SOUND_CAST_HOLY], VOL_NORM, ATTN_NORM, 0, PITCH_NORM )

	set_task( 0.0, szTaskString, _, sParam, sParamSize )
}



//-------------------------------------------
// PLAYER_RemoveSpellCasting
//
//-------------------------------------------
PLAYER_RemoveSpellCasting( id )
{
	PlayerInfo[id][PI_CastingSpell] = false
	PlayerInfo[id][PI_CastingInterrupted] = false
	
	remove_task( TASK_CASTINGSPELL + id )
	remove_task( TASK_CASTINGINTERRUPTCHECK + id )
}



//-------------------------------------------
// PLAYER_SpellAborted
//
//-------------------------------------------
PLAYER_SpellAborted( id, const iSkillID, const Type, const CastType = CAST_NORMAL )
{
	if( CastType == CAST_PASSIVE )
		return
		
	switch( Type )
	{
		case SPELL_ERROR_COOLDOWN:
		{
			LANG_GetSkillName( iSkillID, id, szTmpMsg, 63 )

			WCG_StatusText( id, 0, HUD_CASTING, "%s is on cooldown. (%d seconds left)", \
					szTmpMsg, PlayerInfo[id][PI_SpellCooldown+(S_PLAYERINFO:iSkillID)] )
		}

		case SPELL_ERROR_PLANTDEFUSE:
			WCG_StatusText( id, 0, HUD_CASTING, "You can't cast spells right now!" )
		
		case SPELL_ERROR_STUNNED:
			WCG_StatusText( id, 0, HUD_CASTING, "You can't cast when you're stunned!" )
		
		case SPELL_ERROR_HEXED:
			WCG_StatusText( id, 0, HUD_CASTING, "You can't cast when you're hexed!" )

		case SPELL_ERROR_POLYMORPHED:
			WCG_StatusText( id, 0, HUD_CASTING, "You can't cast when you're polymorphed!" )

		case SPELL_ERROR_NO_MANA:
			WCG_StatusText( id, 0, HUD_CASTING, "Not enough mana!" )
	}

	client_cmd( id, "spk %s", g_szSounds[SOUND_ERROR] )
} 



//-------------------------------------------
// _PLAYER_SearchPing
//
//-------------------------------------------
public _PLAYER_SearchPing( param[] )
{
	new id = param[0]
	new iTimeLeft = param[1]
	
	// Decrement our timer
	param[1]--
	
	// This is the last "playing" of the sound, no target was found :/
	if( !is_user_alive(id) || iTimeLeft == 0 || !PlayerInfo[id][PI_IsSearching] )
	{
		PlayerInfo[id][PI_IsSearching] = false
		ULT_Icon( id, ICON_SHOW )
		return
	}
	
	// Then we need to play the sound
	if( PlayerInfo[id][PI_IsSearching] )
	{
		// Play the ping sound
		emit_sound( id, CHAN_STATIC, g_szSounds[SOUND_ULTIMATESCAN], 1.0, ATTN_NORM, 0, PITCH_NORM )
		set_task( 1.0, "_PLAYER_SearchPing", TASK_SEARCHPING + id, param, 2 )
	}
}



//------------------------------------
// PLAYER_EnhancedGrenadeAllowed
//
// Function checks to see if enhanced grenades are allowed
//------------------------------------
PLAYER_EnhancedGrenadeAllowed( id )
{
	// Need to do the glove check
	if ( ITEM_Has( id, ITEM_GLOVES ) > ITEM_NONE && !get_pcvar_num( CVAR_glove_enh_grenades ) )
		return false

	// Check to see if we are on a disabled map
	if ( GameInfo[GI_DisableEnhGrenades] )
		return false

	return true
}
